﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once

#include "GcmpModel.h"
#include "GcmpApiDeclaration.h"

namespace gcmp 
{ 
    class IDocument;
    class IFamily;
    class IFamilyLoadOptions;
    class IFamilyServiceProvider;
    class IMergeFamilyCategory2HostDocCallback;

    /// \brief 加载族文件返回信息
    enum class LoadFamilyError : int32_t
    {
        NoError = 0b0,                                // 无错误
        CanNotGetFamilyElementsLock = 0b1,            // 无法获得需要修改的Family或者InstanceType锁
        CanNotGetSomeInstanceLock = 0b10,             // 无法获得部分需要更新的Instance的锁，存在部分Instance
        HaveNewerFamilyVersionInDoc = 0b100           // 在当前文档中有更新的族对象，请加载最新的族资源
    };

    /// \brief 加载族文件信息
    struct LoadFamilyResult
    {
        LoadFamilyError LoadFamilyInCloudError = LoadFamilyError::NoError; // 加载族文件错误
        std::set<ElementId> NoPrivilegeInstanceIds;                     // 未能取得加载权限的Instance对象Id
        std::set<ElementId> NoPrivilegeFamilyIds;                       // 未能取得加载权限的Family、InstanceType等对象Id
    };

    /// \brief 获得构件编辑许可回调函数
    /// \param std::map<ElementId, bool>& 待获得编辑许可的ElementId与获得编辑许可结果集合
    /// \return bool 查询的element是否全部拥有编辑许可
    using HasEditPrivilege = std::function<bool(std::map<ElementId, bool>&)>;

    /// \brief 加载族文件信息
    struct LoadFamilyInfos
    {
        LoadFamilyInfos(const std::wstring& path, Int32 version = -1, Int64 Id = -1, HasEditPrivilege function = nullptr)
            :FilePath(path), FamilyVersionInCloud(version), FamilyIdInCloud(Id), hasEditPrivilege(function)
        {}

        std::wstring FilePath;           // 加载的族文件路径
        Int32 FamilyVersionInCloud;      // 构件级协同下该族在云端的版本，仅构件级协同下设置
        Int64 FamilyIdInCloud;           // 构件级协同下该族在云端的Id，仅构件级协同下设置
        HasEditPrivilege hasEditPrivilege; // 构件级协同下获取Element编辑权限回调函数，仅构件级协同下设置
    };


    /// \brief 加载构件级协同族文件返回信息
    enum class UpdateInstanceAndFamilyError : int32_t
    {
        NoError = 0b0,                                // 无错误
        CanNotGetFamilyElementsLock = 0b1,            // 无法获得需要修改的Family或者InstanceType锁
        CanNotGetSomeInstanceLock = 0b10,             // 无法获得部分需要更新的Instance的锁，存在部分Instance
        HaveNewerFamilyVersionInDoc = 0b100,          // 在当前文档中有更新的族对象，请加载最新的族资源
        NotInCollabrationEnvironment = 0b1000,        // 不在构件级协同环境下
        NoFamilyService = 0b10000,                    // 请先加载族服务
        NotLatestFamilyService = 0b100000             // 请先加载最新的族服务
    };

    /// \brief 更新族和实例返回结果
    struct UpdateFamilyAndInstanceResult
    {
        std::set<ElementId> FailingUpdateInstanceIds;     // 更新失败的Instance的ElementId
        UpdateInstanceAndFamilyError UpdateError;            // 更新失败原因
    };
    
    /// \brief 族文件相关的Utility函数集合
    class GCMP_MODEL_EXPORT FamilyUtils
    {
    public:
        /// \brief 加载族
        ///
        /// 如果是构件级协同下加载，将会先获取相关Element的权限
        /// 如果无法获取族相关Element的权限，则加载失败
        /// 如果无法获取Instance相关的权限，则加载部分成功，仅仅有权限的Instance获得更新
        /// 构件级协同下只允许加载最新版本族对象对应的族资源
        /// \param pDoc 文档指针
        /// \param pProvider 族服务提供者指针
        /// \param loadFamilyInfos 加载族文件信息
        /// \param errorMessage 输出参数，如果加载失败， 失败的信息
        /// \param pFamilyLoadOptions 可选参数， 加载时的信息输入
        /// \param pLoadFamilyResult 非构件级协同下可选；构件级协同下为必选参数， 加载构件级协同族，将返回必要信息，see struct LoadFamilyResults
        /// \return 如果成功， 则返回Family的指针， 如果失败， 则为nullptr
        static IFamily* LoadFamily(IDocument* pDoc, const LoadFamilyInfos& loadFamilyInfos, std::wstring& errorMessage, IFamilyLoadOptions * pFamilyLoadOptions, LoadFamilyResult* pLoadFamilyResult = nullptr);

        /// \brief 更新族和实例到最新版本
        /// \param pDoc 文档指针
        /// \param familyName 族名称
        /// \param hasEditPrivilege 获得Element编辑权限回调函数
        /// \return void
        static void UpdateFamilyAndInstanceToLatest(gcmp::IDocument* pDoc, std::wstring familyName, HasEditPrivilege hasEditPrivilege, UpdateFamilyAndInstanceResult& result);

        /// \brief 启动族服务
        ///
        /// \return 如果成功， 返回true， 如果失败，返回false
        static bool StartFamilyServiceProcess();

        /// \brief 加载族并且只创建一个族类型
        ///
        /// \param pDoc 文档指针
        /// \param loadFamilyInfos 加载族文件信息
        /// \param errorMessage 输出参数，如果加载失败， 失败的信息
        /// \param pFamilyLoadOptions 加载时的信息输入
        /// \param pLoadFamilyResult 非构件级协同下可选；构件级协同下为必选参数， 加载构件级协同族，将返回必要信息，see struct LoadFamilyResults
        /// \return 如果成功， 则返回Family的指针， 如果失败， 则为nullptr
        /// \note 创建的族类型会根据族服务返回的第一个类型创建
        static IFamily* LoadFamilyWithFirstType(IDocument* pDoc, const LoadFamilyInfos&  loadFamilyInfos, std::wstring& errorMessage, IFamilyLoadOptions * pFamilyLoadOptions, LoadFamilyResult* pLoadFamilyResult = nullptr);


        /// \brief 重新加载族
        ///
        /// \param pDoc 文档指针
        /// \param loadFamilyInfos 加载族文件信息
        /// \param errorMessage 输出参数，如果加载失败， 失败的信息
        /// \param pLoadFamilyResult 非构件级协同下可选；构件级协同下为必选参数， 加载构件级协同族，将返回必要信息，see struct LoadFamilyResults
        /// \return 如果成功， 则返回Family的指针， 如果失败， 则为nullptr
        static IFamily* ReloadFamily(IDocument* pDoc, const LoadFamilyInfos&  loadFamilyInfos, std::wstring& errorMessage, LoadFamilyResult* pLoadFamilyResult = nullptr);

        /// \brief 重命名族， 如果新的族名字存在， 则失败
        ///
        /// \param pDoc 文档指针
        /// \param oldFamilyName 老的族的名字
        /// \param newFamilyName 新的族的名字
        /// \return 如果成功， 则返回true， 如果失败， 则返回false
        static bool RenameFamily(IDocument* pDoc, const std::wstring& oldFamilyName, const std::wstring& newFamilyName);

        /// \brief 族文件另存为
        ///
        /// \param pDoc 文档指针
        /// \param familyName 要保存的族的名字
        /// \param filePath 保存的路径
        /// \return 如果成功， 则返回true， 如果失败， 则返回false
        static bool SaveFamilyFileAs(IDocument* pDoc, const std::wstring& familyName, const std::wstring& filePath);

        /// \brief 删除族
        ///
        /// \param pDoc 文档指针
        /// \param familyName 要删除的族的名字
        /// \return 如果成功， 则返回true， 如果失败， 则返回false
        static bool DeleteFamily(IDocument* pDoc, const std::wstring& familyName);

        /// \brief 设置族类别与主文档类别映射回调
        ///
        /// \param pDoc 回调所属的文档
        /// \param opCallback 回调类
        static void SetMergeFamilyCategory2HostDocCallback(IDocument* pDoc, OwnerPtr<IMergeFamilyCategory2HostDocCallback> opCallback);

        /// \brief 得到族类别与主文档类别映射回调
        ///
        /// \param pDoc 回调所属的文档
        /// \return 族类别与主文档类别映射回调
        static const IMergeFamilyCategory2HostDocCallback* GetMergeFamilyCategory2HostDocCallback(const IDocument* pDoc);
      
    };
}// namespace gcmp

